package com.office.office.oo;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.office.config.oo.FileConfig;
import com.office.config.oo.convert.ConvertBody;
import com.office.config.oo.document.DocumentPermission;
import com.office.config.oo.edit.FileUser;
import com.office.core.CommonConfig;
import com.office.core.context.FileMetadata;
import com.office.exception.OfficeException;
import com.office.tools.FileUtil;
import com.office.tools.RandomKey;
import com.office.tools.SecurityUtils;
import com.office.tools.oo.JWTUtil;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.*;

/*
 * @BelongsProject: office
 * @BelongsPackage: com.office.office.only
 * @Author: TongHui
 * @CreateTime: 2024-03-14 09:57
 * @Description: TODO
 * @Version: 1.0
 */
@Slf4j
public class OnlyOfficeAPI extends AbstractOOBase{

    public OnlyOfficeAPI(CommonConfig configuration) {
        super(configuration);
    }


    /**
     * @param map                  文件信息
     * @param map{                 必填 fileId
     *                             必填 fileName
     *                             必填 fileType
     *                             必填 fileSize
     *                             可用携带其它信息
     *                             }
     * @param mode                 打开方式 edit/view
     * @param collaborativeEditing 是否协同编辑
     * @param documentPermission   权限信息 非必填
     * @return
     */
    public Map openDocument(Map<String, Object> map, String mode, boolean collaborativeEditing, DocumentPermission documentPermission) {
        long fileSize = (long) map.get("fileSize");

        if (fileSize > configuration.getMaxSize()) {
            // 添加操作日志
            throw new RuntimeException("文件超过【" + configuration.getMaxSize() / 1024 / 1024 + "MB】无法打开");
        }

        FileConfig fileConfigDTO = null;
        if (EDIT.equals(mode)) {
            fileConfigDTO = openEditConfig(map, EDIT, collaborativeEditing,documentPermission);
        }
        if (VIEW.equals(mode)) {
            fileConfigDTO = openEditConfig(map, VIEW, false,documentPermission);
        }

        String json = JSONUtil.toJsonStr(fileConfigDTO);
        Map<String, Object> config = JSONUtil.toBean(json, Map.class);

        return config;
    }

    public void documentSave(HttpServletRequest request, HttpServletResponse response) throws Exception{
        PrintWriter writer = null;
        try {
            writer = response.getWriter();
            // 获取传输的json数据
            Scanner scanner = new Scanner(request.getInputStream()).useDelimiter("\\A");
            String body = scanner.hasNext() ? scanner.next() : "";
            JSONObject jsonObject = JSONUtil.toBean(body,JSONObject.class);
            log.info("{}", jsonObject);

           documentSave(jsonObject);
        } catch (Exception e) {
            e.printStackTrace();
            writer.write("{\"error\":-1}");
            return;
        }
        /*
         * status = 1，我们给onlyOffice的服务返回{"error":"0"}的信息。
         * 这样onlyOffice会认为回调接口是没问题的，这样就可以在线编辑文档了，否则的话会弹出窗口说明
         */
        if (Objects.nonNull(writer)) {
            writer.write("{\"error\":0}");
        }
    }
    /**
     *  文档服务器 保存文件回调
     */
    public void documentSave(JSONObject jsonObject) throws Exception{
        String key = "";
        int status = jsonObject.getInt("status");
        log.info("status[{}]", status);
        if (6 == status) {
            log.info("开始保存文件");

            // 获取修改人 id 通过id获取用户信息，并存储
            List<String> jsonArray = jsonObject.get("users",List.class);
            String userId = jsonArray.get(0);
            FileUser fileUser = new FileUser();
            fileUser.setId(userId);
            SecurityUtils.setUserSession(fileUser);

            //处理文件的保存.
            handlerStatus(jsonObject);
            log.info("保存文件结束");
            SecurityUtils.removeUserSession();
        } else if (0 == status || 2 == status || 4 == status || 3 == status) {
            close(jsonObject);
        } else if (7 == status) {
            //保存文档错误
            log.error("【{}】====={}",status,"强制保存失败");
        } else if (1 == status) {
            //文件服务的回调  获取key判断当前文档有多少人在这使用
            List<Map> actions = jsonObject.getBeanList("actions", Map.class);
            if ((Integer) actions.get(0).get("type") == 1) {
                log.info("当前用户有[{}]", jsonObject.getStr("users"));
                List<String> users = jsonObject.getBeanList("users", String.class);
                iskey(jsonObject.getStr("key"), users.size());
            }
            if ((Integer) actions.get(0).get("type") == 0) {
                iskey(jsonObject.getStr("key"), null);
            }
        }
    }
    /**
     * 处理保存文件
     */
    public void handlerStatus(JSONObject jsonObject) throws Exception {
        log.info("开始下载编辑器文件");
        int status = jsonObject.getInt("status");
        log.info("status[{}]:{}", status, jsonObject);
        String key = (String) jsonObject.get("key");

        FileMetadata tempFile = getTempFile(key);
        if (Objects.nonNull(tempFile)) {
            String url = jsonObject.getStr("url");
            String changesurl = jsonObject.getStr("changesurl");
            log.info("编辑后的文档下载路径url:" + url);
            log.info("文件变动信息文件url:" + changesurl);
            // 下载修改后文件
            byte[] fileByte = FileUtil.getFileByte(url);
            // 下载前一个文化和修改后文件的区别
            byte[] changes = FileUtil.getFileByte(changesurl);
            log.info("下载编辑器文件成功");

            log.info("保存文件前置处理");
            try {
                String fileExtension = FileUtil.getFileExtension(url);

                getSaveFileProcessor().saveBeforeInitialization(tempFile.getFileInfo(), fileByte, fileExtension,this);

                // 保存文件
                Map<String, Object> map = getSaveFileProcessor().save(tempFile.getFileInfo(), fileByte, changes, key,this);

                getSaveFileProcessor().saveAfterInitialization(tempFile.getFileInfo(), fileByte, fileExtension,this);

                tempFile.setFileInfo(map);

                cache.set(getServerName()+key,tempFile);
            } catch (Exception e) {
                throw e;
            }
        } else {
            throw new OfficeException("文件源信息不存在");
        }
    }

    /*
     * //#请求参数示例 https://www.songbin.top/post_27457.html
     * {
     * "filetype": "xlsx",
     * "key": "Khirz6zTPdfd7",
     * "outputtype": "pdf",
     * "region": "en-US",
     * "spreadsheetLayout": {
     * "ignorePrintArea": true,
     * "orientation": "portrait",
     * "fitToWidth": 0,
     * "fitToHeight": 0,
     * "scale": 100,
     * "headings": false,
     * "gridLines": false,
     * "pageSize": {
     * "width": "210mm",
     * "height": "297mm"
     * },
     * "margins": {
     * "left": "17.8mm",
     * "right": "17.8mm",
     * "top": "19.1mm",
     * "bottom": "19.1mm"
     * }
     * },
     * "title": "Example Document Title.pdf",
     * "url": "https://example.com/url-to-example-spreadsheet.xlsx"
     * }
     */
    public String converted(String filetype, String key, String outputtype, String title, String password) {
        log.info("文件开始转化{}->{}", filetype, outputtype);

        String fileUrl = configuration.getDowloadFile() + key;

        ConvertBody body = new ConvertBody(filetype, RandomKey.SnowflakeId(), outputtype, fileUrl, title, password,convert);
        String bodyString = JSONUtil.toJsonStr(body);

        if (null != onlyProperties.getSecret()) {
            String token = JWTUtil.createToken(JSONUtil.toBean(bodyString, Map.class), onlyProperties.getSecret());
            body.setToken(token);
            bodyString = JSONUtil.toJsonStr(body);
        }

        log.info(bodyString);
        byte[] bodyByte = bodyString.getBytes(StandardCharsets.UTF_8);
        try {
            boolean async = body.getAsync() == null ? false : body.getAsync();
            Long resultPercent = 0L;
            String responseUri = null;
            do {
                URL url = new URL(onlyProperties.getDocService() + onlyProperties.CONVERTER);
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setRequestMethod("POST");
                connection.setDoOutput(true);
                connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
                connection.setFixedLengthStreamingMode(bodyByte.length);
                connection.setRequestProperty("Accept", "application/json");
                connection.setConnectTimeout(configuration.getTimeout());

                connection.connect();

                OutputStream os = connection.getOutputStream();
                os.write(bodyByte);

                InputStream stream = connection.getInputStream();

                if (stream == null) {
                    throw new Exception("Could not get an answer");
                }

                /**将流转为字符串*/
                String jsonString = FileUtil.ConvertStreamToString(stream);

                connection.disconnect();

                JSONObject jsonObj = JSONUtil.parseObj(jsonString);
                log.info(jsonObj.toString());
                /**
                 * {
                 *     "endConvert":true，//转换是否完成
                 *     "fileUrl"：“ https：//documentserver/ResourceService.ashx?filename=output.doc”，//转换后的文件地址
                 *     "percent"：100//转换完成百分比 仅参数设置为异步时
                 *  }
                 */
                Object error = jsonObj.get("error");
                if (error != null) {
                    ProcessConvertServiceResponceError((Integer) error);
                }

                /**检查转换是否完成，并将结果保存到一个变量中*/
                Boolean isEndConvert = (Boolean) jsonObj.get("endConvert");

                if (isEndConvert) {
                    resultPercent = 100L;
                    responseUri = (String) jsonObj.get("fileUrl");
                    log.info("文件转化完成{}->{}", filetype, outputtype);
                } else {
                    resultPercent = jsonObj.getLong("percent");
                    resultPercent = resultPercent >= 100l ? 99l : resultPercent;
                }

            } while (async && resultPercent < 100L);
            return responseUri;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }


    /*
     * https://documentserver/coauthoring/CommandService.ashx
     * {
     * "c": "forcesave",
     * "key": "Khirz6zTPdfd7",
     * "userdata": "sample userdata"
     * }
     */
    public String save(String key, String userId) {
        Map<String, Object> map = new HashMap<>();
        map.put("c", "forcesave");
        map.put("key", key);
        map.put("userdata", userId);
        if (null != onlyProperties.getSecret()) {
            String token = JWTUtil.createToken(map, onlyProperties.getSecret());
            map.put("token", token);
        }
        String bodyString = JSONUtil.toJsonStr(map);
        log.info("forcesave:" + bodyString);
        byte[] bodyByte = bodyString.getBytes(StandardCharsets.UTF_8);
        try {
            URL url = new URL(onlyProperties.getDocService() + onlyProperties.SAVE);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("POST");
            connection.setDoOutput(true);
            connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
            connection.setFixedLengthStreamingMode(bodyByte.length);
            connection.setRequestProperty("Accept", "application/json");
            connection.setConnectTimeout(configuration.getTimeout());

            connection.connect();

            OutputStream os = connection.getOutputStream();
            os.write(bodyByte);

            InputStream stream = connection.getInputStream();

            if (stream == null) {
                throw new Exception("Could not get an answer");
            }

            /**将流转为字符串*/
            String jsonString = FileUtil.ConvertStreamToString(stream);
            connection.disconnect();
            JSONObject jsonObj = JSONUtil.parseObj(jsonString);
            log.info(jsonObj.toString());
            Object error = jsonObj.get("error");
            String msg = "";
            if (error != null) {
                msg = saveMessage((Integer) error);
            }
            return msg;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }


    public void close(JSONObject jsonObject) {
        String key = jsonObject.getStr("key");
        // 判断临时信息是否存在
        FileMetadata tempFile = getTempFile(key);

        //判断 删除时间 是否大于 文件打开时间
        //如果 删除时间 小于 文件打开时间 不执行删除
        if (tempFile == null || new Date().getTime() < tempFile.getOpenTime()) {
            return;
        }

        int i = iskey(key, null);
        if (i <= 0) {
            removeTempFile(jsonObject);
            // 文件id
            String fileid = (String) cache.get("getID_" + key);

            cache.remove("getID_" + key);
            cache.remove("collaborativeEditing_" + fileid);

            List<Map> jsonArray = jsonObject.get("actions", List.class);
            String userId = (String) jsonArray.get(0).get("userid");
            cache.remove(userId + "_" + fileid);
        }
    }

}
